/*
 * SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

use crate::cli::ZipArgs;
use crate::error::ZipError;
use crate::utils::common::{
    copy_local_file_to_zip, copy_zip_file_to_zip, filter_filesystem_files,
    get_input_archive_comment, init_run_state, prepare_files_map, search_pattern_in_archive,
    set_file_comment, should_update_file, RunState,
};
#[allow(unused_imports)]
use crate::zip::{FileOptions, ZipArchive, ZipFile, ZipWriter};
#[allow(unused_imports)]
use anyhow::{Context, Result};
use log::{debug, warn};
use std::fs;
use std::path::PathBuf;

use crate::utils::common::FileCompressionTracker;

#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct SyncInfo {
    pub file_path: PathBuf,
    pub name: String,
    original_size: u64,
    compressed_size: u64,
}

#[derive(Debug, Default)]
#[allow(dead_code)]
struct TotalCount {
    files_map: std::collections::BTreeMap<String, PathBuf>,
    processed_files: std::collections::HashSet<String>,
    files_completed: usize,
    added_count: usize,
    updated_count: usize,
    unchanged_count: usize,
    deleted_count: usize,
    total_count: usize,
}

pub fn execute(args: &ZipArgs) -> Result<()> {
    if args.zipfile.is_none() {
        return Err(ZipError::InvalidArguments("No zip file specified".to_string()).into());
    }

    let zipfile = args.zipfile.as_ref().unwrap();

    if !zipfile.exists() {
        warn!(
            "Zip file not found, creating new archive: {}",
            zipfile.display()
        );
        return crate::commands::add::execute(args);
    }

    let mut run_state = init_run_state(args)?;

    let mut total_count = TotalCount::default();
    let file_selects = match prepare_files_map(args, &args.command, run_state.zip_file_tmp.as_ref())
    {
        Ok(files) => files,
        Err(e) => {
            // Update模式下，如果文件不存在，需要在zip文件的现有条目中搜索匹配的模式
            if e.to_string().contains("file_not_found:") {
                // 从错误消息中提取文件名模式
                let error_msg = e.to_string();
                if let Some(start) = error_msg.find("file_not_found: ") {
                    let pattern = &error_msg[start + "file_not_found: ".len()..];
                    // 在zip文件的现有条目中搜索匹配的模式
                    search_pattern_in_archive(&mut run_state, pattern, args)?
                } else {
                    std::collections::BTreeMap::new()
                }
            } else {
                return Err(e);
            }
        }
    };
    // 对于从归档搜索得到的文件，跳过文件系统过滤
    total_count.files_map = if file_selects
        .values()
        .any(|path| path.to_string_lossy().starts_with("__ZIP_ENTRY__:"))
    {
        // 包含归档搜索结果，直接使用，不进行文件系统过滤
        file_selects
    } else {
        // 普通文件系统文件，进行正常过滤
        filter_filesystem_files(&file_selects, args)
    };

    run_state.init_display_info(&total_count.files_map, &args.command)?;

    debug!("About to call process_existing_files");
    process_existing_files(args, &mut run_state, &mut total_count)?;

    debug!("About to call add_new_files");
    add_new_files(args, &mut run_state, &mut total_count)?;

    if args.basic_options.add_comments {
        log::info!("Adding comments to files...");
        set_file_comment(&mut run_state)?;
    }

    if args.basic_options.add_archive_comment {
        log::info!("Adding archive comments...");
        let mut writer = run_state.writer.take().expect("Writer not initialized");
        let old_comments = writer.get_archive_comment();
        if !old_comments.is_empty() && !args.basic_options.quiet {
            println!("current zip file comment is: \n{}", old_comments);
        }
        let comments = get_input_archive_comment()?;
        writer.set_archive_comment(&comments);
        run_state.writer = Some(writer);
    }

    run_state.finish()?;

    // 删除所有已记录的目录
    for dir in &run_state.dirs_to_remove {
        if dir.exists() && dir.is_dir() {
            // 检查目录是否为空
            let is_empty = fs::read_dir(dir)?.next().is_none();
            if is_empty {
                debug!("Removing directory: {}", dir.display());
                if let Err(e) = fs::remove_dir_all(dir) {
                    warn!("Failed to remove directory {}: {}", dir.display(), e);
                    return Err(anyhow::Error::msg(format!(
                        "Failed to remove directory {}: {}",
                        dir.display(),
                        e
                    )));
                }
            } else {
                warn!("Directory {} is not empty, skipping removal", dir.display());
            }
        }
    }

    if !args.basic_options.quiet {
        log::info!(
            "Success! {} updated {} files, added {} files, deleted {} files, skipped {} unchanged files",
            zipfile.display(),
            total_count.updated_count,
            total_count.added_count,
            total_count.deleted_count,
            total_count.unchanged_count
        );

        if args.basic_options.verbose {
            run_state.print_summary_verbose();
        }
    }

    Ok(())
}

fn process_existing_files(
    args: &ZipArgs,
    run_state: &mut RunState,
    total_count: &mut TotalCount,
) -> Result<()> {
    debug!("process_existing_files called");
    debug!("files_map: {:?}", total_count.files_map);

    // 检查是否有文件匹配
    if total_count.files_map.is_empty() && !args.files.is_empty() {
        // 如果指定了文件但没有匹配到，这里不再显示警告
        // 因为警告已经在search_pattern_in_archive或prepare_files_map中显示过了
        log::info!("No files matched the specified patterns, no files to process");

        // 不要添加归档中的文件到files_map，因为这会导致在add_new_files中尝试添加不存在的文件
        // 直接返回，不处理任何文件
    } else if total_count.files_map.is_empty() {
        // 如果没有指定文件，处理归档中的所有文件
        log::info!("No files specified, processing all archive files");
        let archive = run_state.archive.take().expect("Archive not initialized");
        for i in 0..archive.len() {
            let file = archive.by_index_raw(i)?;
            let name = file.name().to_string();
            total_count
                .files_map
                .insert(name.clone(), PathBuf::from(&name));
        }
        run_state.archive = Some(archive);
    }

    let mut writer = run_state.writer.take().expect("Writer not initialized");

    if let Some(archive) = run_state.archive.take() {
        for i in 0..archive.len() {
            let file = archive.by_index_raw(i)?;
            let name = file.name().to_string();
            debug!("processing: {}", name);

            // 处理文件路径
            let file_path: PathBuf = if total_count.files_map.is_empty() {
                // 如果没有指定文件，尝试使用zip中的文件名作为路径
                PathBuf::from(&name)
            } else if total_count.files_map.contains_key(&name) {
                // 如果指定了文件，使用指定的路径
                total_count.files_map.get(&name).unwrap().clone()
            } else {
                // 不在更新列表中的文件
                handle_file_not_in_map(args, &file, &mut writer, total_count, run_state)?;
                run_state.total_original_size += file.origin_size();
                run_state.total_compressed_size += file.compressed_size();
                continue;
            };

            // 检查是否是从zip条目中匹配的文件
            let file_path_str = file_path.to_string_lossy();
            if let Some(stripped) = file_path_str.strip_prefix("__ZIP_ENTRY__:") {
                // 这是从zip条目中匹配的文件，检查文件系统中是否有对应的文件
                let actual_name = stripped;
                let actual_path = PathBuf::from(actual_name);

                if actual_path.exists() {
                    // 文件系统中有对应的文件，使用实际路径进行更新检查
                    debug!("zip entry {} has corresponding file in filesystem", name);
                    // 直接处理更新逻辑，不需要重新插入files_map
                    let should_update = should_update_file(&file, &actual_path)?;
                    if should_update {
                        debug!("needs update: {}", name);
                        run_state
                            .update_display_info(writer.get_current_split_index(), &actual_path);
                        let action = if args.basic_mode_options.freshen {
                            "freshening"
                        } else {
                            "updating"
                        };
                        run_state.print_operation_start(action, &name)?;

                        debug!(
                            "Attempting to copy file from {} to zip entry {}",
                            actual_path.display(),
                            name
                        );
                        debug!("actual_path exists: {}", actual_path.exists());
                        debug!("actual_path is_file: {}", actual_path.is_file());
                        match copy_local_file_to_zip(
                            run_state,
                            &mut writer,
                            &actual_path,
                            name.clone(),
                        ) {
                            Ok(tracker) => {
                                run_state.total_original_size += tracker.original_size;
                                run_state.total_compressed_size += tracker.compressed_size;
                                run_state.changed_files.push(name.clone());
                                run_state.print_operation_end(&tracker);
                                total_count.updated_count += 1;
                                total_count.processed_files.insert(name.clone());
                            }
                            Err(e) => {
                                warn!("Failed to copy file {}: {}", name, e);
                                return Err(e);
                            }
                        }
                    } else {
                        debug!("no need update: {}", name);
                        if args.basic_options.verbose {
                            println!("zip diagnostic: {} up to date", name);
                        }
                        copy_zip_file_to_zip(run_state, &file, &mut writer)?;
                        total_count.processed_files.insert(name.clone());
                        total_count.unchanged_count += 1;
                    }
                } else {
                    // 文件系统中没有对应的文件，保持zip中的条目不变
                    debug!(
                        "zip entry {} has no corresponding file in filesystem, keeping unchanged",
                        name
                    );
                    copy_zip_file_to_zip(run_state, &file, &mut writer)?;
                    total_count.unchanged_count += 1;
                    // 标记为已处理，避免在add_new_files中再次处理
                    total_count.processed_files.insert(name.clone());
                }
                continue;
            }

            // 检查是否是特殊标记路径
            let file_path_str = file_path.to_string_lossy();
            if file_path_str.starts_with("__ZIP_ENTRY__:") {
                // 这种情况应该已经在上面处理过了，如果到这里说明有逻辑错误
                warn!(
                    "Unexpected special marker path in normal processing: {}",
                    file_path_str
                );
                copy_zip_file_to_zip(run_state, &file, &mut writer)?;
                total_count.unchanged_count += 1;
                continue;
            }

            if file_path.exists() {
                // 普通的文件系统文件
                debug!("file: {} exits", name);
                total_count.files_map.insert(name.clone(), file_path);
            } else if args.basic_mode_options.filesync {
                // filesync模式下删除不存在的文件
                run_state.print_operation_start("deleting", &name)?;
                total_count.deleted_count += 1;
                continue;
            } else {
                // 非filesync模式下保留文件
                copy_zip_file_to_zip(run_state, &file, &mut writer)?;
                total_count.unchanged_count += 1;
                continue;
            }

            // 处理在更新列表中的文件
            if let Err(e) = handle_file_in_map(args, &mut writer, &file, run_state, total_count) {
                if args.basic_mode_options.filesync {
                    // filesync模式下跳过无效文件
                    if !args.basic_options.quiet {
                        debug!("err: {}", e);
                        run_state.print_operation_start("deleting", &name)?;
                    }
                    continue;
                }
                return Err(e);
            }
        }
        run_state.archive = Some(archive);
    }

    run_state.writer = Some(writer);
    Ok(())
}

fn handle_file_not_in_map(
    args: &ZipArgs,
    file: &ZipFile,
    zip: &mut ZipWriter,
    total_count: &mut TotalCount,
    run_state: &mut RunState,
) -> Result<()> {
    let name = file.name().to_string();
    debug!("not in map: {}", name);
    if args.basic_mode_options.filesync {
        run_state.print_operation_start("deleting", &name)?;
        let tracker = &FileCompressionTracker {
            disk_num: zip.get_current_split_index(),
            original_size: file.origin_size(),
            compressed_size: file.compressed_size(),
            ratio: 0.0,
            method: file.compression_method(),
        };
        run_state.print_operation_end(tracker);

        total_count.deleted_count += 1;
        return Ok(());
    }

    copy_zip_file_to_zip(run_state, file, zip)?;
    total_count.unchanged_count += 1;
    Ok(())
}

fn handle_file_in_map(
    args: &ZipArgs,
    zip_writer: &mut ZipWriter,
    file: &ZipFile,
    run_state: &mut RunState,
    total_count: &mut TotalCount,
) -> Result<()> {
    let name = file.name().to_string();
    let file_path = total_count
        .files_map
        .get(&name)
        .ok_or_else(|| anyhow::anyhow!("File not found in map: {}", name))?;

    // 检查是否是特殊标记路径
    let file_path_str = file_path.to_string_lossy();
    if file_path_str.starts_with("__ZIP_ENTRY__:") {
        warn!(
            "Unexpected special marker path in handle_file_in_map: {}",
            file_path_str
        );
        copy_zip_file_to_zip(run_state, file, zip_writer)?;
        total_count.processed_files.insert(name.to_string());
        total_count.unchanged_count += 1;
        return Ok(());
    }

    // 处理目录
    // if file_path.is_dir() {
    //     copy_zip_file_to_zip(run_state, file, zip_writer)?;
    //     total_count.processed_files.insert(name.to_string());
    //     total_count.unchanged_count += 1;
    //     return Ok(());
    // }
    let should_update = should_update_file(file, file_path)?;
    if should_update {
        debug!("needs update: {}", name);
        run_state.update_display_info(zip_writer.get_current_split_index(), file_path);
        let action = if args.basic_mode_options.freshen {
            "freshening"
        } else {
            "updating"
        };
        run_state.print_operation_start(action, &name)?;

        debug!(
            "handle_file_in_map: attempting to copy file from {} to zip entry {}",
            file_path.display(),
            name
        );
        debug!("file_path exists: {}", file_path.exists());
        debug!("file_path is_file: {}", file_path.is_file());
        match copy_local_file_to_zip(
            run_state,
            zip_writer,
            &file_path.to_path_buf(),
            name.to_string(),
        ) {
            Ok(tracker) => {
                run_state.total_original_size += tracker.original_size;
                run_state.total_compressed_size += tracker.compressed_size;
                run_state.changed_files.push(name.clone());

                debug!(
                    "File {}: Original Size: {}, Compressed Size: {}, Compression Ratio: {:.2}%",
                    name, tracker.original_size, tracker.compressed_size, tracker.ratio
                );
                run_state.print_operation_end(&tracker);

                total_count.updated_count += 1;
                total_count.processed_files.insert(name.to_string());

                if args.basic_options.move_files {
                    // 如果是文件，直接删除
                    if file_path.is_file() {
                        debug!("delete original file {}", file_path.display());
                        if let Err(e) = fs::remove_file(file_path) {
                            warn!(
                                "Failed to delete original file {}: {}",
                                file_path.display(),
                                e
                            );
                            return Err(anyhow::Error::msg(format!(
                                "Failed to delete original file {}: {}",
                                file_path.display(),
                                e
                            )));
                        }
                    }
                    // 如果是目录，记录到待删除目录列表
                    else if file_path.is_dir() {
                        run_state.dirs_to_remove.insert(file_path.clone());
                    }
                }
            }
            Err(e) => {
                warn!("Failed to copy file {}: {}", name, e);
                return Err(e);
            }
        }
    } else {
        debug!("no need update: {}", name);
        if args.basic_options.verbose {
            println!("zip diagnostic: {} up to date", name);
        }
        copy_zip_file_to_zip(run_state, file, zip_writer)?;
        total_count.processed_files.insert(name.to_string());
        total_count.unchanged_count += 1;
    }

    Ok(())
}

fn add_new_files(
    args: &ZipArgs,
    run_state: &mut RunState,
    total_count: &mut TotalCount,
) -> Result<()> {
    debug!("add_new_files called");
    debug!("files_map: {:?}", total_count.files_map);
    debug!("processed_files: {:?}", total_count.processed_files);

    // 在freshen模式下跳过添加新文件
    if args.basic_mode_options.freshen {
        return Ok(());
    }
    let mut writer = run_state.writer.take().expect("Writer not initialized");

    for (name, file_path) in total_count.files_map.iter() {
        if !total_count.processed_files.contains(name) {
            // 跳过从zip条目匹配的特殊路径
            let file_path_str = file_path.to_string_lossy();
            if file_path_str.starts_with("__ZIP_ENTRY__:") {
                debug!("Skipping zip entry marker path: {}", file_path_str);
                continue;
            }

            run_state.update_display_info(writer.get_current_split_index(), file_path);
            run_state.print_operation_start("adding", name)?;
            match copy_local_file_to_zip(run_state, &mut writer, file_path, name.clone()) {
                Ok(tracker) => {
                    run_state.total_original_size += tracker.original_size;
                    run_state.total_compressed_size += tracker.compressed_size;
                    run_state.changed_files.push(name.clone());

                    debug!("File {}: Original Size: {}, Compressed Size: {}, Compression Ratio: {:.2}%", name, tracker.original_size, tracker.compressed_size, tracker.ratio);
                    run_state.print_operation_end(&tracker);

                    total_count.added_count += 1;

                    if args.basic_options.move_files {
                        // 如果是文件，直接删除
                        if file_path.is_file() {
                            debug!("delete original file {}", file_path.display());
                            if let Err(e) = fs::remove_file(file_path) {
                                warn!(
                                    "Failed to delete original file {}: {}",
                                    file_path.display(),
                                    e
                                );
                                return Err(anyhow::Error::msg(format!(
                                    "Failed to delete original file {}: {}",
                                    file_path.display(),
                                    e
                                )));
                            }
                        }
                        // 如果是目录，记录到待删除目录列表
                        else if file_path.is_dir() {
                            run_state.dirs_to_remove.insert(file_path.clone());
                        }
                    }
                }
                Err(e) => {
                    warn!("Failed to copy file {}: {}", name, e);
                    return Err(e);
                }
            }
        }
    }

    run_state.writer = Some(writer);
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::cli::{BasicModeOptions, BasicOptions, EncryptionOptions};
    use chrono::{Duration, Local};
    use std::fs;
    use std::path::Path;
    use tempfile::TempDir;

    // 创建测试文件及目录结构的辅助函数
    fn setup_test_files(temp_dir: &Path) -> Result<()> {
        // 创建测试文件
        fs::write(temp_dir.join("file1.txt"), "original content")?;
        fs::write(temp_dir.join("file2.txt"), "content to update")?;

        // 创建测试目录及其内容
        fs::create_dir(temp_dir.join("testdir"))?;
        fs::write(temp_dir.join("testdir/file3.txt"), "nested file")?;

        Ok(())
    }

    // 创建测试用的ZIP文件
    fn create_test_zip(zip_path: &Path, temp_dir: &Path) -> Result<()> {
        let mut writer = ZipWriter::new(zip_path.to_str().unwrap())?;

        let file1_path = temp_dir.join("file1.txt");
        let file2_path = temp_dir.join("file2.txt");
        // 添加文件到ZIP
        let options = FileOptions::new();
        writer.start_file(file1_path.to_str().unwrap(), options.clone())?;
        writer.write_all(b"original content")?;

        writer.start_file(file2_path.to_str().unwrap(), options)?;
        writer.write_all(b"old content")?;

        writer.finish()?;
        Ok(())
    }

    #[test]
    fn test_execute_update() -> Result<()> {
        // 1. 准备测试环境
        let temp_dir = TempDir::new().context("创建临时目录失败")?;
        let zip_path = temp_dir.path().join("test.zip");

        // 创建测试文件路径
        let original_file = temp_dir.path().join("original.txt");
        let updated_file = temp_dir.path().join("updated.txt");

        let original_file_path = original_file.to_str().unwrap();
        let updated_file_path = updated_file.to_str().unwrap();

        // 2. 创建初始ZIP文件
        {
            let mut writer =
                ZipWriter::new(zip_path.to_str().unwrap()).context("创建ZipWriter失败")?;
            let options = FileOptions::new();

            // 添加两个测试文件
            writer
                .start_file(original_file_path, options.clone())
                .context("添加original.txt失败")?;
            writer.write_all(b"original content")?;

            writer
                .start_file(updated_file_path, options)
                .context("添加updated.txt失败")?;
            writer.write_all(b"old content")?;

            writer.finish().context("完成ZIP写入失败")?;
        }

        // 3. 更新本地文件
        fs::write(&original_file, "original content").context("写入original.txt失败")?;
        fs::write(&updated_file, "new updated content").context("写入updated.txt失败")?;

        // 修改文件修改时间以确保更新
        let future_time = Local::now() + Duration::hours(1);
        filetime::set_file_mtime(
            &updated_file,
            filetime::FileTime::from_system_time(future_time.into()),
        )
        .context("设置文件修改时间失败")?;

        // 4. 执行更新操作
        let args = ZipArgs {
            zipfile: Some(zip_path.clone()),
            files: vec![temp_dir.path().to_path_buf()],
            basic_options: BasicOptions {
                recurse: true,
                quiet: true,
                ..Default::default()
            },
            ..Default::default()
        };

        execute(&args).context("执行更新失败")?;

        // 5. 验证结果
        let mut archive = ZipArchive::new(zip_path.to_str().unwrap()).context("打开ZIP文件失败")?;

        // 验证未修改的文件
        let original_content = archive.by_name(original_file_path)?;
        assert_eq!(
            String::from_utf8(archive.read_file(&original_content, None)?)?,
            "original content",
            "original.txt内容不应改变"
        );

        // 验证已更新的文件
        let updated_content = archive.by_name(updated_file_path)?;
        assert_eq!(
            String::from_utf8(archive.read_file(&updated_content, None)?)?,
            "new updated content",
            "updated.txt内容应已更新"
        );

        Ok(())
    }

    #[test]
    fn test_filesync_mode() -> Result<()> {
        let temp_dir = TempDir::new()?;
        let zip_path = temp_dir.path().join("test.zip");

        let original_file = temp_dir.path().join("file1.txt");
        let updated_file = temp_dir.path().join("file2.txt");
        let original_file_path = original_file.to_str().unwrap();
        let updated_file_path = updated_file.to_str().unwrap();

        // 设置初始文件
        setup_test_files(temp_dir.path())?;
        create_test_zip(&zip_path, temp_dir.path())?;

        // 删除一个本地文件
        fs::remove_file(original_file.as_path())?;

        // 构造filesync模式的参数
        let args = ZipArgs {
            zipfile: Some(zip_path.clone()),
            files: vec![temp_dir.path().to_path_buf()],
            basic_mode_options: BasicModeOptions {
                filesync: true,
                ..Default::default()
            },
            basic_options: BasicOptions {
                recurse: true,
                quiet: true,
                ..Default::default()
            },

            ..Default::default()
        };

        // 执行同步
        execute(&args)?;

        // 验证结果
        let mut archive = ZipArchive::new(zip_path.to_str().unwrap())?;
        assert!(
            archive.by_name(original_file_path).is_err(),
            "删除的文件应该不存在"
        );
        assert!(
            archive.by_name(updated_file_path).is_ok(),
            "未删除的文件应该存在"
        );

        Ok(())
    }

    #[test]
    fn test_encrypted_update() -> Result<()> {
        let temp_dir = TempDir::new()?;
        let zip_path = temp_dir.path().join("test.zip");

        let original_file = temp_dir.path().join("file1.txt");
        let original_file_path = original_file.to_str().unwrap();

        // 设置测试文件
        setup_test_files(temp_dir.path())?;

        // 构造加密更新的参数
        let args = ZipArgs {
            zipfile: Some(zip_path.clone()),
            files: vec![temp_dir.path().to_path_buf()],
            basic_mode_options: BasicModeOptions {
                ..Default::default()
            },
            basic_options: BasicOptions {
                recurse: true,
                quiet: true,
                ..Default::default()
            },
            encryption: EncryptionOptions {
                password: Some("secret".to_string()),
                ..Default::default()
            },
            ..Default::default()
        };

        // 执行加密更新
        execute(&args)?;

        // 验证加密结果
        let mut archive = ZipArchive::new(zip_path.to_str().unwrap())?;
        let encrypted_file = archive.by_name(original_file_path)?;
        assert!(encrypted_file.encrypted(), "文件应该被加密");

        // 使用密码读取文件
        let contents = archive.read_file(&encrypted_file, Some("secret"))?;
        assert_eq!(String::from_utf8(contents)?, "original content");

        Ok(())
    }
}
